package math2;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.pow;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;

import java.util.ArrayList;

/**
 * This class provides the four crack tip functions.
 * 
 * @author Hannah
 */
public class CrackTipBasis extends FunctionBasisOnRnToR {

	public CrackTipBasis() {
		basis = new ArrayList<FunctionRnToR>();
		addBasisFunction(new F(0), new F(1), new F(2), new F(3));
	}

	private class F extends FunctionRnToR {

		private final int idx;

		public F(int idx) {
			this.idx = idx;
		}

		@Override
		public double[] gradientAt(double... xi) {
			double[] res = new double[2];

			switch (this.idx) {
			case 0:
				res[0] = -(xi[1] * cos(atan2(xi[1], xi[0]) / 2.) * pow(pow(xi[0], 2)
						+ pow(xi[1], 2), -0.75))
						/ 2.
						+ (xi[0] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
								xi[0]) / 2.)) / 2.;
				res[1] = (xi[0] * cos(atan2(xi[1], xi[0]) / 2.) * pow(
						pow(xi[0], 2) + pow(xi[1], 2), -0.75))
						/ 2.
						+ (xi[1] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
								xi[0]) / 2.)) / 2.;
				break;
			case 1:
				res[0] = (xi[0] * cos(atan2(xi[1], xi[0]) / 2.) * pow(
						pow(xi[0], 2) + pow(xi[1], 2), -0.75))
						/ 2.
						+ (xi[1] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
								xi[0]) / 2.)) / 2.;
				res[1] = (xi[1] * cos(atan2(xi[1], xi[0]) / 2.) * pow(
						pow(xi[0], 2) + pow(xi[1], 2), -0.75))
						/ 2.
						- (xi[0] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
								xi[0]) / 2.)) / 2.;
				break;
			case 2:
				res[0] = -(xi[1] * cos(atan2(xi[1], xi[0]))
						* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1], xi[0]) / 2.))
						- (xi[1] * cos(atan2(xi[1], xi[0]) / 2.)
								* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
									xi[0])))
						/ 2.
						+ (xi[0] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
								* sin(atan2(xi[1], xi[0]) / 2.) * sin(atan2(xi[1], xi[0]))) / 2.;
				res[1] = xi[0]
						* cos(atan2(xi[1], xi[0]))
						* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
						* sin(atan2(xi[1], xi[0]) / 2.)
						+ (xi[0] * cos(atan2(xi[1], xi[0]) / 2.)
								* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
									xi[0])))
						/ 2.
						+ (xi[1] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
								* sin(atan2(xi[1], xi[0]) / 2.) * sin(atan2(xi[1], xi[0]))) / 2.;
				break;
			case 3:
				res[0] = -(xi[1] * cos(atan2(xi[1], xi[0]) / 2.) * cos(atan2(xi[1], xi[0])) * pow(
						pow(xi[0], 2) + pow(xi[1], 2), -0.75))
						+ (xi[0] * cos(atan2(xi[1], xi[0]) / 2.)
								* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
									xi[0])))
						/ 2.
						+ (xi[1] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
								* sin(atan2(xi[1], xi[0]) / 2.) * sin(atan2(xi[1], xi[0]))) / 2.;
				res[1] = xi[0]
						* cos(atan2(xi[1], xi[0]) / 2.)
						* cos(atan2(xi[1], xi[0]))
						* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
						+ (xi[1] * cos(atan2(xi[1], xi[0]) / 2.)
								* pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75) * sin(atan2(xi[1],
									xi[0])))
						/ 2.
						- (xi[0] * pow(pow(xi[0], 2) + pow(xi[1], 2), -0.75)
								* sin(atan2(xi[1], xi[0]) / 2.) * sin(atan2(xi[1], xi[0]))) / 2.;
				break;
			default:
				throw new Error();
			}
			return res;
		}

		@Override
		public double valueAt(double... xi) {
			double r = sqrt(pow(xi[0], 2) + pow(xi[1], 2));
			double phi = atan2(xi[1], xi[0]);
			double f = 0;

			switch (this.idx) {
			case 0:
				f = sqrt(r) * sin(phi / 2);
				break;
			case 1:
				f = Math.sqrt(r) * Math.cos(phi / 2);
				break;
			case 2:
				f = Math.sqrt(r) * Math.sin(phi / 2) * Math.sin(phi);
				break;
			case 3:
				f = Math.sqrt(r) * Math.cos(phi / 2) * Math.sin(phi);
				break;
			default:
				throw new Error();
			}
			return f;
		}
	}

	@Override
	public Object clone() {
		return new CrackTipBasis();
	}

}
